#include "data.hpp"
#include "util.hpp"
#include "mongoose.h"
#include <unordered_map>
#include <time.h>


namespace im_sys{
#define SERVER_PORT 20000
#define WWWROOT "./wwwroot/"
#define OFFLINE 0
#define ONLINE 1
	struct IMSession{
		public:
			int status;
			std::string  session_id;//系统时间
			std::string username;
			time_t ctime;//绘画创建时间
			time_t ltime;//最后操作时间
			struct mg_connection *conn;//当前用户对应的mongoose连接
	};
	class Server{
		private:
			static struct mg_mgr *_mgr;
			static UserTable *_user;
			static std::unordered_map<std::string ,IMSession> _session;//保存所有的会话信息
		private:
			static std::string ConResp(bool res,const std::string  &info){
				Json::Value err;
				err["result"]= res ;
				err["reason"]= info ;
				std::string body;
				JsonUtil::Serialize(err,&body);

				return body;
			}
			static std::string  CreateIMSession(struct mg_connection *c,const std::string &username){

				struct IMSession s;
				s.conn=c;
				s.username=username;
				s.status=ONLINE;
				s.ctime=time(NULL);
				s.ltime=time(NULL);
				
				s.session_id=std::to_string(time(NULL));
				_session[s.session_id]=s;
				return s.session_id;
			}
			static void Login(struct mg_connection *c,struct mg_http_message *hm){
				//拿到请求正文，进行json解析，json反序列化得到用户名与密码

				Json::Value user_info;
				JsonUtil::UnSerialize(hm->body.ptr,&user_info);
				std::string username=user_info["username"].asString();
				std::string password=user_info["password"].asString();
				//在数据库中进行用户名与密码的验证
				bool ret = _user->UserPassCheck(username,password);
				if(ret==false){
					std::string err_body=ConResp(false,"用户名密码错误");
					std::string header="Content-Type:application/json\r\n";
					mg_http_reply(c,400,header.c_str(),err_body.c_str());
					return ;
				}
				//判断用户是否已经在线--已经在线就不能登录
				ret=_user->Status(username);
				if(ret == true){
					std::string body=ConResp(false,"用户已经登录");
					std::string header="Content-Type:application/json\r\n";
					mg_http_reply(c,400,header.c_str(),body.c_str());
					return ;
				}
				//没有在线则，修改用户为在线状态，并且返回登陆成功,设置setcookie，用户名用户登陆状态
				std::string ssid=CreateIMSession(c,username);
				ret=_user->Update_Status(username,ONLINE);
				if(ret==false){

					std::string body=ConResp(false,"修改用户状态失败");
					std::string header="Content-Type:application/json\r\n";
					mg_http_reply(c,500,header.c_str(),body.c_str());
					return ;
				}
				//登陆成功则需要让用户端去请求一个聊天页面
				std::stringstream headers;
				headers <<  "Location: /chat.html\r\n";
				//聊天页面
				headers << "Set-Cookie: SSID=" << ssid << "; Path=/\r\n";
				std::string body=ConResp(true,"登录成功");
				mg_http_reply(c,302,headers.str().c_str(),body.c_str());
			}




			static void Register(struct mg_connection *c,struct mg_http_message *hm){
				//注册请求
				//拿到请求信息的正文，是json格式的用户信息，进行解读
				Json::Value user_info;
				JsonUtil::UnSerialize(hm->body.ptr,&user_info);
				std::string username=user_info["username"].asString();
				std::string password=user_info["password"].asString();
				
				//在数据库中进行查看用户是否已经被占用
				bool ret = _user->Exists(username);
				if(ret==true){
					Json::Value err;
					err["result"]=false;
					err["reason"]="用户名已被占用";
					std::string header="Content-Type:application/json\r\n";
					std::string body;
					JsonUtil::Serialize(err,&body);
					mg_http_reply(c,400,header.c_str(),body.c_str());
					return ;
				}
				//返回结果--登陆成功/登陆失败
				//mg_http_reply(c,200,"","register sucess!");
				ret=_user->Insert(username,password);
				if(ret==false){
					Json::Value err;
					err["result"]=false;
					err["reason"]="用户信息插入数据库失败";
					std::string header="Content-Type:application/json\r\n";						
					std::string body;
					JsonUtil::Serialize(err,&body);
					mg_http_reply(c,500,header.c_str(),body.c_str());
					return ;
				}
				ret=_user->Insert(username,password);
				Json::Value err;
				err["result"]=true;
				err["reason"]="成功！";
				std::string header="Content-Type:application/json\r\n";
				std::string body;
				JsonUtil::Serialize(err,&body);
				mg_http_reply(c,200,header.c_str(),body.c_str());
				
				return ;

			}
			static bool GetSession(struct mg_connection *c,IMSession *session){
				auto it=_session.begin();
				for(;it!=_session.end();++it){
					if(it->second.conn == c){
						*session = it->second;
						return true;
					}
				}
				return false;
			}


			static bool GetSessionBySSID(struct mg_connection *c,const std::string &ssid,struct IMSession *session){
				auto it=_session.begin();
				for(;it!=_session.end();++it){
					std::cout<<"session:["<<it->first<<"]<-->["<<ssid<<"]\n";
					if(it->first==ssid){
						*session=it->second;
						return true;
					}
				}
				return false;
			}
			static bool DelSession(IMSession &session){
				auto it = _session.find(session.session_id);
				if(it==_session.end()){
					std::cout<<"not find session!\n";
					return false;
				}
				_session.erase(it);
				return true;
			}
			static void BroadcastMsg(const std::string &msg){
				//_mgr->conns
				for(struct mg_connection *c=_mgr->conns; c!=NULL ;c=c->next){
					//websocket发送消息,同时判断当前连接是否为websocket的连接
					if(c->is_websocket){
						int i=mg_ws_send(c,msg.c_str(),msg.size(),WEBSOCKET_OP_TEXT);
						std::cout<<"broadcastMSG ok"<<msg<<msg.c_str()<<msg.size()<<"发送字节为："<<i<<std::endl;
					}
				}
				return ;
			}
			static void ConnClose(struct mg_connection *c){
				//找到连接对应的session
				IMSession session;
				bool ret=GetSession(c,&session);
				if(ret==false){
					std::cout<<" close connect have no session info!\n";
					return ;
				}
				//修改数据库中的用户状态
				ret=_user->Update_Status(session.username,OFFLINE);
				if(ret==false){
					std::cout<<"update status offline faileed!\n";
					return ;
				}
				//删除会话
				DelSession(session);
				std::string body=session.username+"离开了聊天室";
				BroadcastMsg(body);
			}


			static void WSHandShake(struct mg_connection *c){
				IMSession session;
				bool ret=GetSession(c,&session);
				if(ret==false){
					std::cout<<"websocket handshake have no session info!\n";
					return ;
				}
				std::string body="欢迎[";
				body+=session.username;
				body+="]进入聊天室！";

				BroadcastMsg(body);
				std::cout<<body<<std::endl;
				return ;
			}





			static void Chat(struct mg_connection *c,struct mg_ws_message *wm){
				std::string msg=wm->data.ptr;
				IMSession session;
				bool ret=GetSession(c,&session);
				if(ret==false){
					std::cout<<"chat have no session info!\n";
					return ;
				}
				std::string body =session.username;
				body+=":";
				body+=msg;
				std::cout<<"Chat show"<<body<<std::endl;
				BroadcastMsg(body);
				return ;
			}

			static void callback(struct mg_connection *c ,int ev,void * ev_data,void *fn_data)
			{
				if(ev==MG_EV_HTTP_MSG){
					struct mg_http_message *hm=(struct mg_http_message*)ev_data;
					if(mg_http_match_uri(hm,"/register")){
						//注册请求
						Register(c,hm);
					}else if(mg_http_match_uri(hm,"/login")){
						//登录请求
						Login(c,hm);
					}else if(mg_http_match_uri(hm,"/cws")){
						//协议切换请求
						struct mg_str *cookie = mg_http_get_header(hm, "Cookie");
						struct mg_str token = mg_str(": ");
						if (cookie == NULL) {
							//没有找到cookie
							return ;
						}
							  token = mg_http_get_header_var(*cookie, mg_str("SSID"));
							  std::string ssid;
							  ssid.assign(token.ptr,token.len);
							  std::cout<<"ssid:"<<ssid<<std::endl;

						IMSession session;
						bool ret=GetSessionBySSID(c,ssid,&session);
						if(ret==false){
							std::cout<<" websocket ---have no session info!\n";
							return ;
						}
						std::cout<<"websocket---user"<<session.username<<std::endl;
						CreateIMSession(c,session.username);
						mg_ws_upgrade(c,hm,NULL);
					}else {
						//静态页面请求
						struct mg_http_serve_opts opts={.root_dir=WWWROOT};
						mg_http_serve_dir(c,hm,&opts);
					}
				}
				else if(ev==MG_EV_WS_OPEN){
					//websocket握手成功
					//像聊天室广播一条消息--欢迎xxx进入聊天
					std::cout<<"websocket handshake over\n";
					WSHandShake(c);

				}else if(ev==MG_EV_WS_MSG){
					//收到聊天消息
					//像聊天室的所有人广播一下收到的一条消息
					std::cout<<"websocket recv mgs\n";
					struct mg_ws_message *wm=(struct mg_ws_message *) ev_data;
					Chat(c,wm);

				}else if(ev==MG_EV_CLOSE){
					//连接断开
					ConnClose(c);
				}
			}

		public:
			Server(){
				_mgr=new struct mg_mgr();
				_user=new UserTable();
				mg_mgr_init(_mgr);
			}
			~Server(){
				mg_mgr_free(_mgr);
				delete _mgr;
				delete _user;
			}
			bool RunModule(int port = SERVER_PORT){
				std::string addr="0.0.0.0:";
				addr+=std::to_string(port);
				auto res=mg_http_listen(_mgr,addr.c_str(),callback,_mgr);
				if(res==NULL){
					std::cout<<"init http listen failed!\n";
					return false;
				}
				while(1){
					mg_mgr_poll(_mgr,1000);
				}
				return true;
			}
	};
	struct mg_mgr * Server::_mgr=NULL;
	UserTable * Server::_user=NULL;
	std::unordered_map<std::string,IMSession> Server::_session;
}
